package com.eliza.base.views.learn.animation

import androidx.compose.animation.Animatable
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.ExperimentalTransitionApi
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.TargetBasedAnimation
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateRect
import androidx.compose.animation.core.createChildTransition
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Phone
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp


object AnimateValueX {
    @Composable
    fun Show() {
        var statex by remember {
            mutableIntStateOf(0)
        }
        Column {
            Button(onClick = {
                statex = statex.plus(1)
            }) {
                Text(text = "click to change$statex")
            }
            when (statex % 5) {
                0 -> AnimateAsStateX()
                1 -> AnimateTransitionX()
                2 -> DialerX(BoxState.Collapsed)
                3 -> AnimationX()
                4 -> AnimationTransitionX(BoxState.Collapsed)
            }
        }
    }

    @Composable
    fun AnimateAsStateX() {
//        用于为单个值添加动画效果。您只需提供目标值（或结束值），该 API 就会从当前值开始向指定值播放动画
        val enabled by remember { mutableStateOf(true) }

        val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f)
        Box(
            Modifier
                .fillMaxSize()
                .graphicsLayer(alpha = alpha)
                .background(Color.Red)
        ) {
            Text(text = "animateAsState 只需提供目标值（或结束值")
        }
    }

    @Composable
    fun AnimateTransitionX() {

        val Collapsed = 0
        val Expanded = 1

        val currentState by remember { mutableStateOf(Collapsed) } // 任意类型
        //updateTransition 会创建并记住 Transition 的实例，并更新其状态。
        val transition = updateTransition(currentState, label = "box state")
        if (transition.targetState == Expanded) {
            println("过度完成")
        }

        ///初始状态与第一个目标状态不同。我们可以通过结合使用 updateTransition 和 MutableTransitionState 来实现这一点

        //-----------
        val rect by transition.animateRect(label = "rectangle") { state ->
            when (state) {
                Collapsed -> Rect(0f, 0f, 100f, 100f)
                else -> Rect(100f, 100f, 300f, 300f)
            }
        }
        val borderWidth by transition.animateDp(label = "border width") { state ->
            when (state) {
                Collapsed -> 1.dp
                else -> 0.dp
            }
        }
        //传递 transitionSpec 参数，为过渡状态变化的每个组合指定不同的 AnimationSpec
        val color by transition.animateColor(
            transitionSpec = {
                when {
                    Expanded isTransitioningTo Collapsed ->
                        spring(stiffness = 50f)

                    else ->
                        tween(durationMillis = 500)
                }
            }, label = "color"
        ) { state ->
            when (state) {
                Collapsed -> MaterialTheme.colorScheme.primary
                else -> MaterialTheme.colorScheme.background
            }
        }
        //过渡到目标状态后，Transition.currentState 将与 Transition.targetState 相同。
        // 这可以用作指示是否已完成过渡的信号


        val currentStateX = remember { MutableTransitionState(Collapsed) } // 任意类型
        currentStateX.targetState = Expanded
        val transitionX = updateTransition(currentState, label = "box state")
    }


    @Composable
    private fun dialerButton(isVisibleTransition: Transition<Boolean>) {
        // `isVisibleTransition` spares the need for the content to know
        // about other DialerStates. Instead, the content can focus on
        // animating the state change between visible and not visible.
        Text(text = "${isVisibleTransition.targetState}")

    }

    @Composable
    private fun dialerNumberPad(isVisibleTransition: Transition<Boolean>) {
        // `isVisibleTransition` spares the need for the content to know
        // about other DialerStates. Instead, the content can focus on
        // animating the state change between visible and not visible.
        Text(text = "${isVisibleTransition.targetState}")
    }

    @OptIn(ExperimentalTransitionApi::class)
    @Composable
    fun DialerX(dialerState: BoxState) {
        val transition = updateTransition(dialerState, label = "dialer state")
        Box {
            // Creates separate child transitions of Boolean type for NumberPad
            // and DialerButton for any content animation between visible and
            // not visible
            dialerNumberPad(
                transition.createChildTransition {
                    it == BoxState.Collapsed
                }
            )
            dialerButton(
                transition.createChildTransition {
                    it == BoxState.Expanded
                }
            )
        }
    }

    @OptIn(ExperimentalAnimationApi::class)
    @Composable

    private fun AnimationX() {
        var selected by remember { mutableStateOf(false) }
        // Animates changes when `selected` is changed.
        val transition = updateTransition(selected, label = "selected state")
        val borderColor by transition.animateColor(label = "border color") { isSelected ->
            if (isSelected) Color.LightGray else Color.DarkGray
        }
        val elevation by transition.animateDp(label = "elevation") { isSelected ->
            if (isSelected) 20.dp else 2.dp
        }
        Surface(
            onClick = { selected = !selected },
            shape = RoundedCornerShape(8.dp),
            border = BorderStroke(2.dp, borderColor),
            tonalElevation = elevation,
            shadowElevation = elevation
        ) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            ) {
                Text(text = "AnimationX")
                // AnimatedVisibility as a part of the transition.
                //AnimatedVisibility 和 AnimatedContent 可用作 Transition 的扩展函数。

                transition.AnimatedVisibility(
                    visible = { targetSelected ->
                        //它将父过渡的目标状态转换为布尔值，而不是接受布尔值 visible 参数。
                        targetSelected
                    },
                    enter = expandVertically(),
                    exit = shrinkVertically()
                ) {
                    Text(text = "It is fine today.")
                }
                // AnimatedContent as a part of the transition.
                transition.AnimatedContent { targetState ->
                    if (targetState) {
                        Text(text = " transition.AnimatedContent")
                    } else {
                        Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone")
                    }
                }
            }
        }
    }

    //封装 Transition 并使其可重复使用
    // Holds the animation values.
    private class TransitionData(
        color: State<Color>,
        size: State<Dp>
    ) {
        val color by color
        val size by size
    }

    @Composable
    private fun updateTransitionData(boxState: BoxState): TransitionData {
        val transition = updateTransition(boxState, label = "box state")
        val color = transition.animateColor(label = "color") { state ->
            when (state) {
                BoxState.Collapsed -> Color.Gray
                BoxState.Expanded -> Color.Red
            }
        }
        val size = transition.animateDp(label = "size") { state ->
            when (state) {
                BoxState.Collapsed -> 64.dp
                BoxState.Expanded -> 128.dp
            }
        }
        return remember(transition) { TransitionData(color, size) }
    }

    @Composable
    private fun AnimationTransitionX(boxState: BoxState) {
        val transitionData = updateTransitionData(boxState)
        // UI tree
        Box(
            modifier = Modifier
                .background(transitionData.color)
                .size(transitionData.size)
        )
    }
}

//Animation 是可用的最低级别的 Animation API。到目前为止
object AnimateValueLowX {
    @Composable
    fun Show() {
        var statex by remember {
            mutableIntStateOf(0)
        }
        Column {
            Button(onClick = {
                statex = statex.plus(1)
            }) {
                Text(text = "click to change$statex")
            }
            when (statex % 5) {
                0 -> AnimatableX(true)

            }
        }
    }

    @Composable
    fun AnimatableX(ok: Boolean) {
        var tempOk = ok
        /*
        * 并记住了初始值为 Color.Gray 的 Animatable 实例。
        * 根据布尔标记 ok 的值，颜色将以动画形式呈现 Color.Green 或 Color.Red。
        * 对该布尔值的任何后续更改都会使动画开始使用另一种颜色。
        * 如果更改该值时有正在播放的动画，系统会取消该动画，并且新动画将以当前速度从当前快照值开始播放。
        * */
        // Start out gray and animate to green/red based on `ok`
        var color = remember { Animatable(Color.Gray) }
        LaunchedEffect(tempOk) {
            color.animateTo(if (tempOk) Color.Green else Color.Red)
            color.snapTo(Color.Blue)
        }
        //Animatable 对内容值提供更多操作（即 snapTo 和 animateDecay）。
        // snapTo 可立即将当前值设为目标值。如果动画本身不是唯一的可信来源，且必须与其他状态（如触摸事件）同步，该函数就非常有用。
        // animateDecay 用于启动播放从给定速度变慢的动画。这有助于实现投掷行为。如需了解详情，请参阅手势和动画。
        Box(
            Modifier
                .fillMaxSize()
                .background(color.value)
        ) {
            Text(text = "Animatable", modifier = Modifier.clickable {
                tempOk = !tempOk
            })
        }

    }

    //Animation 子类型有两种：TargetBasedAnimation 和 DecayAnimation。
    //使用 TargetBasedAnimation 可以直接让您自己控制动画的播放时间
    @Composable
    fun AnimatableXX(ok: Boolean) {
        /*
        * DecayAnimation
            与 TargetBasedAnimation 不同，DecayAnimation 不需要提供 targetValue，
            * 而是根据起始条件（由 initialVelocity 和 initialValue 设置）以及所提供的 DecayAnimationSpec 计算其 targetValue。
        * */

        val anim = remember {
            TargetBasedAnimation(
                animationSpec = tween(200),
                typeConverter = Float.VectorConverter,
                initialValue = 200f,
                targetValue = 1000f
            )
        }
        var playTime by remember { mutableStateOf(0L) }
        LaunchedEffect(anim) {
            val startTime = withFrameNanos { it }
            do {
                playTime = withFrameNanos { it } - startTime
                val animationValue = anim.getValueFromNanos(playTime)
            } while (startTime < 500)
        }
    }
}